První kroky ve světě kvantového počítání
- Obtížnost: Začátečník
- Využití QPU času: 11 s
Vítej, hackere! Jsme nadšeni, že tě máme na workshopu. Hlavním cílem tohoto úvodního praktického cvičení je připravit tě na tvoji kvantovou cestu: 1) provést tě instalací Qiskitu, 2) ukázat, jak si vytvořit účet IBM Cloud a připravit api_key a crn pro použití skutečného kvantového počítače, 3) vytvořit tvůj první kvantový Circuit, 4) vyřešit kviz o kvantových stavech a 5) spustit tvé obvody na skutečném kvantovém počítači a vykreslit výsledky.
1. Ze všeho nejdřív: Qiskit
Co je Qiskit
Qiskit SDK je vysoce výkonný softwarový zásobník vytvořený proto, aby pomohl vývojářům a výzkumníkům plně využít sílu kvantových počítačů v užitném měřítku i za jeho hranicemi. Jádrem je Qiskit SDK, open-source sada nástrojů pro vývoj softwaru určená pro práci s kvantovými počítači na úrovni rozšířených kvantových Circuits, operátorů a primitiv. Qiskit SDK umožňuje komukoli dosáhnout optimálního výkonu na skutečných kvantových počítačích v jejich preferovaném výpočetním prostředí.
Kromě SDK zahrnuje Qiskit také sadu vysoce výkonných nástrojů a služeb, jako je Qiskit Runtime Service, která umožňuje optimalizované výpočty na kvantových počítačích IBM prostřednictvím cloudu pomocí primitiv spravujících potlačení chyb. Dále služba Qiskit Transpiler, jež nabízí nejmodernější heuristické a AI metody zvyšující výkon při běžných úlohách optimalizace kvantových obvodů.
Qiskit functions, katalog IBM a služeb třetích stran, které usnadňují optimalizaci pracovních zátěží a využití Qiskitu pro průmyslové případy použití. Ať už jsi vývojář kvantového softwaru, kvantový experimentátor, výpočetní vědec, nebo jen hledáš, kde začít, modulární a flexibilní framework Qiskitu ti umožňuje pracovat na úrovni abstrakce, která nejlépe vyhovuje tvým potřebám.
Qiskit je navržen s důrazem na rozšiřitelnost a přizpůsobitelnost, takže z něj můžeš vytěžit špičkový výkon a řešit nové druhy problémů. Výkonná kódová základna znamená, že Qiskit SDK pracuje rychleji, spotřebovává méně paměti a přináší lepší výsledky než kdykoli předtím. Qiskit tě také uvádí do rozsáhlé komunity uživatelů a vývojářů, kteří tě rádi přivítají a pomohou ti zodpovědět tvoje otázky. Program Qiskit advocate, spuštěný poprvé v roce 2019, je globální komunitní iniciativa, která získává odborníky a nadšence kvantových výpočtů z celého světa. V průběhu let se z mnohých advokátů stali uznávaní lídři v kvantové komunitě. Chceš být dalším kvantovým lídrem? Neváhej se přihlásit – zde
Instalace Qiskitu
Nejprve ověř, zda verze Pythonu ve tvém prostředí splňuje podmínku python>=3.10, aby byla kompatibilní s nejnovější verzí Qiskitu, kterou budeme používat
from platform import python_version
print(python_version())
Pokud tomu tak není, můžeš Python aktualizovat pomocí preferovaného nástroje. Pokud si nejsi jistý/á, jak na to, zde jsou doporučené možnosti:
- MacOS: Homebrew
- Linux:
sudo apt-get update
Podrobný průvodce aktualizací Pythonu podle tvého operačního systému najdeš zde: How to update Python
Více informací najdeš na wiki QGSS (Qiskit Global Summer School) 2025: https://github.com/qiskit-community/qgss-2025/wiki/Jupyter-Notebook-Environment-(Local-and-Online)
Instalaci ověříš spuštěním buňky níže. Pokud proběhla správně, zobrazí se verze Qiskitu.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
%pip install 'qiskit[visualization]'
%pip install qiskit-ibm-runtime
%pip install qiskit-aer
import qiskit
print(f"Qiskit version: {qiskit.__version__}")
Řešení problémů
Pokud předchozí buňka vyvolala chybu, můžeš Qiskit nainstalovat ve virtuálním prostředí (níže jsou dva navrhované způsoby). Pokud žádná chyba nenastala, tuto buňku přeskoč a pokračuj dál.
Navrhujeme dva různé způsoby, jak nastavit virtuální prostředí pro instalaci Qiskitu.
- Pomocí venv, jak je vysvětleno v průvodci instalací Qiskitu.
- Pomocí conda, jak je ukázáno v tomto videu Coding with Qiskit.
2. Nastavení účtu IBM Cloud
Abys mohl/a používat skutečný kvantový počítač, potřebuješ api key – hlavní vstupní lístek do cloudu – a crn – token, který ti zpřístupní tvé zdroje nastavením účtu.
Účet nastav takto:
- Přejdi na IBM Quantum® Platform.
- Přejdi do pravého horního rohu (jak je znázorněno na obrázku výše), vytvoř svůj API token a ulož ho na bezpečné místo.
- V následující buňce nahraď
deleteThisAndPasteYourAPIKeyHeresvým API klíčem. - Přejdi do levého dolního rohu (jak je znázorněno na obrázku výše) a vytvoř svou instanci. Ujisti se, že jsi zvolil/a plán open.
- Po vytvoření instance zkopíruj přidružený kód CRN. Možná bude nutné stránku obnovit, aby se instance zobrazila.
- V buňce níže nahraď
deleteThisAndPasteYourCRNHeresvým kódem CRN.
Více podrobností o nastavení účtu IBM Cloud® najdeš v tomto průvodci.
⚠️ Poznámka: Zacházej se svým API klíčem jako s bezpečným heslem. Více informací o používání API klíče v bezpečných i nedůvěryhodných prostředích najdeš v průvodci Cloud setup.
Navíc, pokud jsi členem/členkou partnerské sítě IBM pro univerzity, použij prosím institucionální e-mailovou adresu jako své IBM ID, abys získal/a partnerské výhody.
from qiskit_ibm_runtime import QiskitRuntimeService
# Save your API key and crn and have access to the quantum computers
your_api_key = "deleteThisAndPasteYourAPIKeyHere"
your_crn = "deleteThisAndPasteYourCRNHere"
QiskitRuntimeService.save_account(
channel="ibm_cloud",
token=your_api_key,
instance=your_crn,
overwrite=True
)
# Check that the account has been saved properly
service = QiskitRuntimeService()
service.saved_accounts()
# See backends you can use
service.backends()
3. Tvůj první kvantový Circuit
Kvantové obvody
Základní jednotkou Qiskitu je kvantový Circuit, což je série instrukcí, které kvantový počítač využívá k práci s kvantovými bity informace, nazývanými qubity. Tyto qubit mají speciální vlastnosti, díky nimž mohou kvantové počítače přistupovat k problémům odlišně než tvůj laptop nebo iPhone. Qiskit se jako technologie rychle rozvíjí – zatímco stále můžeš ručně navrhovat kvantové obvody a rozhodovat o způsobu jejich spuštění (a je mnoho dobrých důvodů to dělat), IBM Quantum ti také poskytuje nástroje, které celý proces zjednodušují. Pro účely výuky si vytvoříme velmi jednoduchý Circuit a spustíme ho na simulátoru.
Tímto přehledem projdeme rychle, protože kdybychom chtěli, mohli bychom strávit hodiny vysvětlováním základů kvantové informace a výpočtů. Ve skutečnosti to IBM Quantum již udělalo – vznikl psaný kurz i série videolekcí věnovaných tomuto tématu. Pokud potřebuješ osvěžit znalosti, mrkni na ně!
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_state_qsphere
from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.visualization import array_to_latex
from qiskit.visualization import plot_distribution
import numpy as np
from numpy import sqrt
Základní operace s Qubity a měření
Zápis jednoQubitových stavů
Začněme pohledem na jediný qubit. Hlavní rozdíl oproti klasickému bitu, který může nabývat pouze hodnot 0 a 1, spočívá v tom, že kvantový bit neboli qubit může být ve stavech , , ale také v jejich lineární kombinaci. Tato vlastnost se nazývá superpozice a umožňuje nám zapsat nejobecnější stav Qubitu takto:
Kdybychom změřili stav tohoto Qubitu, dostali bychom výsledek s pravděpodobností a výsledek s pravděpodobností . Jak vidíš, celková pravděpodobnost je , což znamená, že naměříme buď , nebo , a žádný jiný výsledek neexistuje.
Kromě si možná všimnout ještě dalšího parametru. Proměnná označuje relativní kvantovou fázi mezi stavy a . Jak zjistíme později, tato relativní fáze je poměrně důležitá. Prozatím stačí poznamenat, že kvantová fáze umožňuje interferenci mezi kvantovými stavy, díky níž můžeme psát kvantové algoritmy pro řešení konkrétních úloh.
Vizualizace kvantových stavů
V tomto cvičení budeme kvantové stavy vizualizovat pomocí tzv. qsphere. Takto vypadá qsphere pro stavy a . Všimni si, že vrchol sféry představuje stav , zatímco spodek odpovídá stavu .
#visualize |0>
sv=Statevector([1, 0])
plot_state_qsphere(sv)
Úplně stejnou QSphere lze vytvořit pomocí kvantového Circuitu. Stavový vektor, který zde používáme, odpovídá stavu . V Qiskitu je qubit inicializován ve stavu . Zkus spustit níže uvedený Circuit a podívej se, zda dostaneš stejnou QSphere.
qc1 = QuantumCircuit(1)
sv=Statevector(qc1)
plot_state_qsphere(sv)
Teď si vizualizujme stav .
Nemělo by být překvapením, že superpozice s kvantovou fází a pravděpodobností (tedy stejnou pravděpodobností naměření 0 i 1) se na qsphere zobrazí jako dva body. Všimni si ale také, že kruhy v těchto dvou bodech jsou menší než v případě, kdy jsme měli pouze a . Je to proto, že velikost kruhů je úměrná pravděpodobnosti naměření příslušného stavu, která je nyní poloviční.
#visualize 1/sqrt(2)|0> + 1/sqrt(2)|1>
sv=Statevector([1/sqrt(2), 1/sqrt(2)])
plot_state_qsphere(sv)
V případě superpozičních stavů s nenulovou kvantovou fází umožňuje qsphere tuto fázi vizualizovat změnou barvy příslušného bodu. Například stav s (stupňů) a pravděpodobností je na qsphere zobrazen níže.
sv=Statevector([1/sqrt(2), 1/sqrt(2)*1j])
plot_state_qsphere(sv)
Manipulace s Qubity
S Qubity manipulujeme pomocí kvantových Gate. Pojďme si udělat přehled různých Gate, se kterými se setkáme v následujících cvičeních.
Nejprve si popišme, jak můžeme změnit hodnotu pro náš obecný kvantový stav. K tomu použijeme dvě Gate:
-
-Gate: Tato Gate přepíná mezi stavy a . Tato operace je analogická klasické hradlu NOT. Proto se -Gate někdy označuje jako bit flip nebo NOT Gate. Matematicky Gate mění na , konkrétně tedy z 0 na 1 a naopak.
-
-Gate: Tato Gate umožňuje přejít ze stavu do stavu . Tento stav se také označuje jako . Matematicky to znamená přechod z na . Protože výsledný stav Qubitu je superpozicí a , představuje Hadamardova Gate skutečnou kvantovou operaci.
Všimni si, že obě Gate změnily hodnotu , ale ne . Naštěstí si působení těchto hradlo celkem snadno vizualizujeme pohledem na obrázek níže.
Jakmile máme stav , můžeme kvantovou fázi měnit pomocí dalších Gate. Například Gate přidá fázi stupňů k , zatímco Gate přidá fázi stupňů k . Abychom odebrali fázi stupňů, můžeme použít Gate, čteno jako S-dagger, běžně zapisované jako sdg. Konečně existuje Gate, která aplikuje sekvenci a Gate.
S Gate , , , , a si můžeš pohrát a zvyknout si na různé operace a na to, jak ovlivňují stav Qubitu. K tomu navštiv Circuit Composer a spusť náš widget Circuit. Po otevření Circuit Composeru vyber Gate, které chceš na qubit aplikovat, a pak zvol qubit (v prvních příkladech je jediným Qubitem qubit 0). Sleduj, jak se příslušný stav mění s každou Gate, a přečti si jeho popis. Composer ti také poskytne kód, který vytváří odpovídající kvantový Circuit v Qiskitu.
Pokud se chceš dozvědět více o popisu kvantových stavů, Pauliho operátorech a dalších jednoQubitových Gate, nahlédni do Quantum Information sekce Single System v kurzu Basics of Quantum Information od Johna Wattouse.
Cvičení: Kvantové Circuits s jednoQubitovými Gate
Zde jsou čtyři malá cvičení, jejichž cílem je dosáhnout různých stavů na qsphere. Můžeš je vyřešit pomocí Circuit Composeru a zkopírovat výsledný kód do příslušných buněk, nebo přímo vkládat kombinaci následujících řádků kódu pro aplikaci různých Gate:
qc.x(0) # bit flip qc.y(0) # bit and phase flip qc.z(0) # phase flip qc.h(0) # superpostion qc.s(0) # quantum phase rotation by pi/2 (90 degrees) qc.sdg(0) # quantum phase rotation by -pi/2 (90 degrees)
Číslice '(0)' označuje, že tuto Gate aplikujeme na qubit 'q0', což je první (a v tomto případě jediný) qubit.
V každém z následujících cvičení se pokus dosáhnout zobrazeného stavu na qsphere.
i) Začněme provedením bit flipu. Cílem je dosáhnout stavu počínaje stavem .
def create_circuit():
qc = QuantumCircuit(1)
#
#
# FILL YOUR CODE IN HERE
#
#
return qc
# check solution
qc2 = create_circuit()
state = Statevector(qc2)
plot_state_qsphere(state, show_state_labels=True, show_state_phases = True, use_degrees = True)
ii) Teď si vytvoříme superpozici. Cílem je dosáhnout stavu .
def create_circuit2():
qc = QuantumCircuit(1)
#
#
# FILL YOUR CODE IN HERE
#
#
return qc
qc3 = create_circuit2()
state = Statevector(qc3)
plot_state_qsphere(state, show_state_labels=True, show_state_phases = True, use_degrees = True)
iii) Zkombinujme oboje. Cílem je dosáhnout stavu .
Dokážeš zkombinovat předchozí dvě úlohy a najít řešení?
def create_circuit3():
qc = QuantumCircuit(1)
#
#
# FILL YOUR CODE IN HERE
#
#
return qc
qc4 = create_circuit3()
state = Statevector(qc4)
plot_state_qsphere(state, show_state_labels=True, show_state_phases = True, use_degrees = True)
iv) Nakonec se přesuneme ke komplexním číslům. Cílem je dosáhnout stavu
def create_circuit4():
qc = QuantumCircuit(1)
#
#
# FILL YOUR CODE IN HERE
#
#
return qc
qc5 = create_circuit4()
state = Statevector(qc5)
plot_state_qsphere(state, show_state_labels=True, show_state_phases = True, use_degrees = True)
4. Kvantový kvíz s víceQubitovými Gate
Skvělá práce! Teď, co jsi pochopil/a jednoQubitové Gate, se podíváme na hradlo operující na více qubitech. Budeš vyzván/a vyřešit 4 kvízy kvantových stavů kombinováním jednoQubitových Gate a víceQubitových Gate. Základní Gate na dvou qubitech jsou dány:
qc.cx(c,t) # controlled-X (= CNOT) gate with control qubit c and target qubit t qc.cz(c,t) # controlled-Z gate with control qubit c and target qubit t qc.swap(a,b) # SWAP gate that swaps the states of qubit a and qubit b
Pokud si chceš přečíst více o různých víceQubitových Gate a jejich vztazích, navštiv Quantum Information pro vícečásticové systémy, v John'ově kurzu Basics of Quantum Information.
Všimni si, že pro dva qubity má obecný stav tvar , kde , , a jsou komplexní čísla, jejichž absolutní hodnoty na druhou udávají pravděpodobnost naměření příslušného stavu; např. by byla pravděpodobnost, že oba qubity skončí ve stavu ‘0’. To znamená, že nyní můžeme mít až čtyři body na qsphere.
Začneme kanonickým dvoukubitovým Gate, Gate řízené negace (také CNOT nebo CX). Zde, stejně jako u všech řízených dvoukubitových Gate, je jeden qubit označen jako „control“ (řídící) a druhý jako „target“ (cílový). Pokud je řídící qubit ve stavu , aplikuje se na cílový qubit identitální Gate , tj. neprovede se žádná operace. Naopak, pokud je řídící qubit ve stavu , provede se na cílovém Qubitu Gate X. Proto je CNOT Gate, pokud jsou oba qubity v jednom ze dvou klasických stavů nebo , omezena na klasické operace.
Tato situace se dramaticky mění, když nejprve aplikujeme Hadamarduv Gate na řídící qubit a přivedeme ho do superpozičního stavu . Akce CNOT Gate na tento neklasický vstup může vytvořit silně provázané stavy mezi řídícím a cílovým Qubitem. Pokud je cílový qubit zpočátku ve stavu , výsledný stav je označen a je jedním z tak zvaných Bellových stavů.
i) Sestrojte Bellův stav .
Pro tento stav bychom měli pravděpodobnost naměřit „00“ a pravděpodobnost naměřit „11“. Výsledky obou qubitů jsou tedy dokonale korelovány.
def create_circuit5():
qc = QuantumCircuit(2)
#
#
# FILL YOUR CODE IN HERE
#
#
return qc
qc6 = create_circuit5()
state = Statevector(qc6) # determine final state after running the circuit
display(array_to_latex(state))
display(plot_state_qsphere(state, show_state_labels=True, show_state_phases = True, use_degrees = True))
qc6.draw(output='mpl') # we draw the circuit
Dál zkus vytvořit stav dokonale antikorelovaných qubitů. Všimni si znaménka mínus, které označuje relativní fázi mezi oběma stavy.
ii) Sestrojte Bellův stav .
def create_circuit6():
qc = QuantumCircuit(2)
#
#
# FILL YOUR CODE IN HERE
#
#
return qc
qc7 = create_circuit6()
state = Statevector(qc7) # determine final state after running the circuit
display(array_to_latex(state))
display(plot_state_qsphere(state, show_state_labels=True, show_state_phases = True, use_degrees = True))
qc7.draw(output='mpl') # we draw the circuit
iii) Je ti dán kvantový Circuit popsaný ve funkci níže. Zaměň stavy prvního a druhého Qubitu, abys dostal/a tuto QSphere.
def create_circuit7():
qc = QuantumCircuit(2)
qc.rx(np.pi/3,0)
qc.x(1)
return qc
qc8 = create_circuit7()
#
#
# FILL YOUR CODE IN HERE
#
#
state = Statevector(qc8) # determine final state after running the circuit
display(array_to_latex(state))
display(plot_state_qsphere(state, show_state_labels=True, show_state_phases = True, use_degrees = True))
qc8.draw(output='mpl') # we draw the circuit
iv) Napiš program od nuly, který vytvoří stav GHZ (na třech qubitech),
def create_circuit8():
#
#
# FILL YOUR CODE IN HERE
#
#
#
return qc
qc9 = create_circuit8()
pub4 = (qc9)
state = Statevector(qc9) # determine final state after running the circuit
display(array_to_latex(state))
display(plot_state_qsphere(state, show_state_labels=True, show_state_phases = True, use_degrees = True))
qc9.draw(output='mpl') # we draw the circuit
5. Spusť svůj Circuit a prohlédni si naměřené výsledky na skutečném kvantovém počítači
Teď víme, jak sestavit kvantový Circuit pro manipulaci se stavovým vektorem qubitů. A je čas na tu nejzajímavější část: spustit ho a podívat se na výstup! Zde se naučíme moderní a efektivní způsob provádění našich Circuit pomocí Qiskit.
Qiskit pattern je obecný rámec pro rozložení problémů specifických pro danou doménu a kontextualizaci potřebných schopností do jednotlivých fází. To umožňuje bezproblémové skládání nových schopností vyvinutých výzkumníky IBM Quantum (a dalšími) a vytváří budoucnost, ve které budou kvantové výpočetní úlohy prováděny výkonnou heterogenní (CPU/GPU/QPU) výpočetní infrastrukturou.
Čtyři kroky Qiskit pattern jsou následující:
- Map problém na kvantové Circuit a operátory
- Optimize pro cílový hardware
- Execute na cílovém hardwaru
- Post-process výsledky
Právě jsme dokončili Step 1: Mapping tím, že jsme sestavili kvantové Circuit pro generování požadovaného kvantového stavu. Teď projíme zbývající kroky, abychom viděli výsledky.
Optimize
Zde nastavíme Backend pro spouštění Circuit – můžeš vybrat QPU s least busy (nejméně zaneprazdněný) ze svých přístupných skupin QPU, nebo jednoduše zvolit simulátor, pokud ti zbývá málo GPU času. Jakmile vyberš Backend, pass_manager transpiluje tvé Circuit do nativních sad Gate vybraného Backend a optimalizuje je, abys dostal/a lepší výsledky. pass_manager lze snadno deklarovat pomocí generate_preset_pass_manager a nastavením optimization_level, přičemž vyšší číslo znamená více optimalizačních kroků.
Dalším krokem je vzrušující věc – spustíme kvantový Circuit pomocí Qiskit Runtime!
Uděláme to pomocí dvou Qiskit primitives:
- Sampler vzorkuje výstupní registr z provedení jednoho nebo více kvantových Circuit. Jeho výstupem jsou počty na jednotlivých měřeních.
- Estimator vypočítává střední hodnotu jednoho nebo více pozorovatelných veličin vzhledem ke stavům generovaným kvantovým Circuit. Jeho výstupem jsou střední hodnoty spolu s jejich standardními chybami.
Zde budeme používat Sampler k provedení našich Circuit. Níže uvedená buňka kódu ti ukáže, jak nejprve definovat Backend a pass manager pro něj. Poté přidá measurement (měření) ke všem Circuit a vytvoří pole kvantových Circuit (pub) k předání do Sampler.
backend=service.least_busy()
#backend=AerSimulator()
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
sampler = Sampler(mode=backend)
pub = []
for qc in circ:
qc.measure_all()
pub.append(pm.run(qc))
Execute
Spusťme naše obvod. V případě, že je ve cloudu dlouhá fronta, vytiskni a ulož job_id pro pozdější použití a zkontroluj job_status. Jakmile uvidíš, že se stav jobu změní na Done, načti výsledek jobu.
job = sampler.run(pub)
job_id = job.job_id()
print(job_id)
job.status()
job_retrived = service.job(job_id)
result = job.result()
Post-process
Posledním krokem je interpretovat vizualizaci pro pochopení kvantových stavů, které jsme vytvořili. Před vykreslením několika grafů načteme všechny počty ze všech Circuit. Poté vytvoříme 4 grafy kategorizací Circuit.
result = job.result()
counts_all = [result[k].data.meas.get_counts() for k in range(9)]
Jednokvantové stavy
plot_distribution([counts_all[0], counts_all[1]], legend =['qc1', 'qc2'])
Superpoziční jednokvantové stavy
plot_distribution([counts_all[2], counts_all[3], counts_all[4]], legend =['qc3', 'qc4', 'qc5'])
Dvoukvantové stavy
plot_distribution([counts_all[5], counts_all[6], counts_all[7]],legend =['qc6', 'qc7', 'qc8'] )
Tříkvantové stavy
plot_distribution(counts_all[8], legend=['qc9'])
Další výzva
Objevil/a jsi šum v experimentálních výsledcích na skutečném Backend? Odstraňování šumu qubitů je jednou z aktivních výzkumných oblastí. Vyzkoušej různé možnosti zmírnění a potlačení chyb v Qiskit Runtime a sleduj, jak se šum ve výsledcích provádění mění! (Poznámka) Tyto možnosti vyžadují více QPU času.
Další informace
import qiskit, qiskit_ibm_runtime
print("Qiskit version:", qiskit.version.get_version_info())
print("Qiskit Runtime version:", qiskit_ibm_runtime.__version__)
Qiskit version: 2.1.1
Qiskit Runtime version: 0.40.1
Vytvořil/a: Sophy Shin
Zkontroloval/a: Nate Earnest-Noble
© IBM Corp., 2025
This is licensed under the Apache License, Version 2.0. You may obtain a copy of this license in the LICENSE file in the root directory of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this must retain this copyright notice, and modified files need to carry a notice indicating that they have been altered from the originals.